SimpleFieldSelector = field_selector {
  id :simple
  desc "Selects the first unsolved field with the least possibilities"
  
  logic {
    select unsolved_fields.min_by { |f| f.possibilities.size }
  }
}


UberFieldSelector = field_selector {
  id :uber
  desc "Selects an empty field within the least possibilities, and within those, with the least possibilities in each of the containing houses"
  
  logic {
    selected_field = catch(:found) do
      (2..9).each do |i|
        possible_fields = fields.select { |f| f.possibilities.size == i }
        field = possible_fields.min_by do |f|
          f.seen_fields_prioritized.inject(0) { |sum, g| sum + g.possibilities.size } # total number of possibilities
        end
        
        throw(:found, field) unless possible_fields.empty?
      end
      
      nil
    end
    
    select(selected_field)
  }
}


RandomFieldSelector = field_selector { # just because I can. This is, of course, totally f*cking useless.
  id :random
  desc "Selects a random field."
  logic { select(unsolved_fields.random) }
}

FirstFieldSelector = field_selector { # not very efficient either
  id :first
  desc "Selects the first available field"
  logic { select(unsolved_fields.first) }
}

LastFieldSelector = field_selector { # or this one
  id :last
  desc "Selects the last available field"
  logic { select(unsolved_fields.last) }
}



NakedSubsetFieldSelector = field_selector { # WARNING: DISAPPOINTING RESULTS!
  # this selector REQUIRES direct elimination or an equivalent rule (with relatively high priority) to be effective
  id :naked_subset
  desc "Selects a field that's part of a naked subset"
  fallback :uber # selector to fall back to if no naked subset was found, can be a FieldSelector object or an id.

  logic {
    selected_field = catch(:found) do
      houses.each do |house|
        [*1..9].power_set(2..4).each do |set|
          possible_fields = house.select { |f| set.include?(f.possibilities) && f.possibilities.size >= 2 }
          throw(:found, possible_fields.first) if possible_fields.size == set.size
        end
      end
      
      nil
    end
    
    select(selected_field)
  }
}

MinimalHouseFieldSelector = field_selector {
  id :minimal_house
  desc "Selects a field for which the sum of possibilities of all three houses is minimal"

  logic {
    selected_field = unsolved_fields.min_by do |f|
      f.seen_fields.sum { |g| g.possibilities.size } # total number of possibilities
    end
    
    select(selected_field)
  }
}

HiddenFieldSelector = field_selector {
  id :hidden
  desc "Prefers almost hidden singles"
  fallback :uber
  
  logic  {
    selected_field = unsolved_fields.min_by do |f|
      f.possibilities.sum do |p|
        # if this possibility is ALMOST alone in one of the containing houses, we can kill all the rest by guessing it
        # count the number of times this possibility occurs in each of the containing houses.
        counts = f.containing_houses.collect do |house|
          house.select { |g| g.possibilities.include?(p) }.size
        end
        
        counts.sort!
        (counts[0]* 9 + counts[1]) * 9 + counts[2]
      end
    end

    select(selected_field)
  }
}
